#include "pipeline.h"

#include <gtest/gtest.h>

#include <string>
#include <vector>

using namespace cfg;

// 预置数组数据的 provider, 用于 array_length 校验器测试
class TestArrayProvider : public IProvider
{
public:
    // 提供三种数组长度样本与一个非数组标量, 供校验器产生边界与类型错误
    bool provide(Builder & builder, const PluginOptions &, std::vector<ConfigError> &) override
    {
        auto arr0 = std::make_shared<Array>();
        auto arr1 = std::make_shared<Array>();
        arr1->items.push_back(int64_t{1});
        auto arr3 = std::make_shared<Array>();
        arr3->items = {int64_t{1}, int64_t{2}, int64_t{3}};
        builder.set("arr0", std::shared_ptr<const Array>(arr0));
        builder.set("arr1", std::shared_ptr<const Array>(arr1));
        builder.set("arr3", std::shared_ptr<const Array>(arr3));
        builder.set("not_arr", std::string("x"));
        return true;
    }
};
REGISTER_PROVIDER("test_array_provider", TestArrayProvider);

// 预置对象数据的 provider, 用于 object_required_keys 校验器测试
class TestObjectProvider : public IProvider
{
public:
    // 提供 root 对象含部分键与一个标量, 供校验器检测缺失键与类型不匹配
    bool provide(Builder & builder, const PluginOptions &, std::vector<ConfigError> &) override
    {
        auto obj = std::make_shared<Object>();
        obj->props["a"] = int64_t{1};
        obj->props["c"] = std::string("x");
        builder.set("root", std::shared_ptr<const Object>(obj));
        builder.set("scalar", std::string("y"));
        return true;
    }
};
REGISTER_PROVIDER("test_object_provider", TestObjectProvider);

// 构造一个内存 provider，直接写入待验证的数据。
class TestInlineProvider : public IProvider
{
public:
    bool provide(Builder & builder,
                 const PluginOptions & options,
                 std::vector<ConfigError> & out_errors) override
    {
        (void)out_errors;
        for (const auto & kv : options)
        {
            // 仅注入简单标量、数组与对象的 JSON 字符串，路径为键名。
            // 支持数据键以 data: 前缀指示写入根下对应键。
            if (kv.first.rfind("data:", 0) == 0)
            {
                const std::string key = kv.first.substr(5);
                // 尝试将 kv.second 解析为简单类型，否则按字符串。
                const std::string & v = kv.second;
                if (v == "null")
                {
                    builder.set(key, nullptr);
                }
                else if (v == "true" || v == "false")
                {
                    builder.set(key, v == "true");
                }
                else
                {
                    // 尝试整数
                    char * endptr = nullptr;
                    long long ival = std::strtoll(v.c_str(), &endptr, 10);
                    if (endptr && *endptr == '\0')
                    {
                        builder.set(key, static_cast<int64_t>(ival));
                    }
                    else
                    {
                        // 尝试浮点
                        char * endptr2 = nullptr;
                        double dval = std::strtod(v.c_str(), &endptr2);
                        if (endptr2 && *endptr2 == '\0')
                        {
                            builder.set(key, dval);
                        }
                        else
                        {
                            builder.set(key, v);
                        }
                    }
                }
            }
        }
        return true;
    }
};

REGISTER_PROVIDER("test_inline_provider", TestInlineProvider);

// number_in_range：命中非数值时报 TypeMismatch/NumberExpected；越界分别报低/高。
TEST(ValidatorsCommonTest, NumberInRangeBoundsAndType)
{
    PipelineSpec spec;
    spec.providers.push_back(
        {"test_inline_provider",
         {{"data:x", "not_number"}, {"data:a", "-1"}, {"data:b", "5"}, {"data:c", "11"}}});
    spec.validators.push_back({"number_in_range", {{"path", "x"}, {"min", "0"}, {"max", "10"}}});
    spec.validators.push_back({"number_in_range", {{"path", "a"}, {"min", "0"}}});
    spec.validators.push_back({"number_in_range", {{"path", "b"}, {"min", "0"}, {"max", "10"}}});
    spec.validators.push_back({"number_in_range", {{"path", "c"}, {"max", "10"}}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    int number_expected = 0, below_min = 0, above_max = 0;
    for (const auto & e : result.errors)
    {
        if (e.code == Errc::NumberExpected)
            number_expected++;
        if (e.message.find("below minimum") != std::string::npos)
            below_min++;
        if (e.message.find("above maximum") != std::string::npos)
            above_max++;
    }
    EXPECT_EQ(number_expected, 1);
    EXPECT_EQ(below_min, 1);
    EXPECT_EQ(above_max, 1);
}

// enum_in_set：包含与不包含，类型自动转字符串对比；不支持类型时报错。
TEST(ValidatorsCommonTest, EnumInSetCoversTypesAndMissing)
{
    PipelineSpec spec;
    spec.providers.push_back(
        {"test_inline_provider", {{"data:s", "red"}, {"data:i", "2"}, {"data:b", "true"}}});

    spec.validators.push_back(
        {"enum_in_set", {{"path", "s"}, {"values", "blue, red , green"}}}); // 命中
    spec.validators.push_back(
        {"enum_in_set", {{"path", "i"}, {"values", "1,2,3"}}}); // 命中（字符串化）
    spec.validators.push_back({"enum_in_set", {{"path", "b"}, {"values", "false"}}}); // 不命中

    PipelineRunner runner;
    auto result = runner.run(spec);

    bool has_not_in_set = false;
    for (const auto & e : result.errors)
    {
        if (e.code == Errc::EnumNotInSet && e.notes && e.notes->find("false") != std::string::npos)
            has_not_in_set = true;
    }
    EXPECT_TRUE(has_not_in_set);
}

// array_length：类型不匹配、过短与过长。
TEST(ValidatorsCommonTest, ArrayLengthTypeAndBounds)
{

    PipelineSpec spec;
    spec.providers.push_back({"test_array_provider", {}});
    spec.validators.push_back({"array_length", {{"path", "not_arr"}}}); // 类型不匹配
    spec.validators.push_back({"array_length", {{"path", "arr0"}, {"min_len", "1"}}}); // 太短
    spec.validators.push_back(
        {"array_length", {{"path", "arr1"}, {"min_len", "1"}, {"max_len", "2"}}});     // 命中
    spec.validators.push_back({"array_length", {{"path", "arr3"}, {"max_len", "2"}}}); // 太长

    PipelineRunner runner;
    auto result = runner.run(spec);

    int not_array = 0, below_min = 0, above_max = 0;
    for (const auto & e : result.errors)
    {
        if (e.message.find("not an array") != std::string::npos)
            not_array++;
        if (e.message.find("below min") != std::string::npos)
            below_min++;
        if (e.message.find("above max") != std::string::npos)
            above_max++;
    }
    EXPECT_EQ(not_array, 1);
    EXPECT_EQ(below_min, 1);
    EXPECT_EQ(above_max, 1);
}

// object_required_keys：对象类型与缺失多个键。
TEST(ValidatorsCommonTest, ObjectRequiredKeysMultipleMissing)
{

    PipelineSpec spec;
    spec.providers.push_back({"test_object_provider", {}});
    spec.validators.push_back(
        {"object_required_keys", {{"path", "root"}, {"keys", "a,b,c,d"}}}); // 缺 b,d
    spec.validators.push_back(
        {"object_required_keys", {{"path", "scalar"}, {"keys", "x"}}}); // 类型不匹配

    PipelineRunner runner;
    auto result = runner.run(spec);

    int missing = 0, not_object = 0;
    for (const auto & e : result.errors)
    {
        if (e.code == Errc::MissingKey)
            missing++;
        if (e.message.find("not an object") != std::string::npos)
            not_object++;
    }
    EXPECT_EQ(missing, 2);
    EXPECT_EQ(not_object, 1);
}